package com.tangly.module.controller;

import com.tangly.common.base.BaseController;
import com.tangly.common.exception.BusinessException;
import com.tangly.common.util.DateUtil;
import com.tangly.common.util.JWTUtil;
import com.tangly.common.util.WebRequestUtil;
import com.tangly.module.entity.UserAuthEntity;
import com.tangly.module.enums.LoginTypeEnum;
import com.tangly.module.enums.UserStatusEnum;
import com.tangly.module.service.ISmsService;
import com.tangly.module.service.IUserAuthService;
import com.tangly.module.util.GetPlaceUtil;
import com.tangly.module.util.PasswordHelper;
import com.tangly.module.vo.AppLoginVO;
import com.tangly.module.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

import static com.tangly.common.util.WebRequestUtil.getOsAndBrowserInfo;

@RestController
@RequestMapping(value = "/app")
@Slf4j
@Api(tags = "APP-账号管理")
public class AppAccountController extends BaseController {

    @Autowired
    private ISmsService smsService;

    @Autowired
    private IUserAuthService userAuthService;

    @Autowired
    private PasswordHelper passwordHelper;

    private final int expireTime = 3000;

    @PostMapping(value = "account/code")
    @ApiOperation(value = "向指定手机号码发送短信验证码")
    public ResultVO<String> code(String phone) {
        String code = RandomStringUtils.random(4, "0123456789");
        smsService.sendCode(phone, code, expireTime);
        log.info("手机号 {} 验证码 {} ", phone, code);
        return ResultVO.success("短信暂时发不了,验证码是: " + code);
//        return ResultVO.success("验证码已发送");
    }

    @PostMapping(value = "account/signup")
    @ApiOperation(value = "注册")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "手机号", paramType = "query", required = true),
            @ApiImplicitParam(name = "password", value = "APP端随机生成的复杂密码,用于自动登录", paramType = "query", required = true),
            @ApiImplicitParam(name = "code", value = "手机验证码,仅手机号注册时需要传", paramType = "query"),
            @ApiImplicitParam(name = "loginType", value = "注册类型", paramType = "query", required = true, dataTypeClass = LoginTypeEnum.class)
    })
    public ResultVO<String> signUp(String username, String password, String code, LoginTypeEnum loginType) {

        if (LoginTypeEnum.PHONE.equals(loginType)) {
            if (smsService.verifyCode(username, code)) {

                String encryptPassword = passwordHelper.encryptPassword(password, username, passwordHelper.generateSalt());
                UserAuthEntity userAuthEntity = UserAuthEntity.builder()
                        .createTime(new Date())
                        .isDelete(false)
                        .loginAccount(username)
                        .loginType(loginType)
                        .loginPassword(encryptPassword) // 明文密码将在registerUserAuth中加密
                        //注册 但是未激活
                        .status(UserStatusEnum.DISABLE)
                        .build();

                userAuthService.insertSelective(userAuthEntity);

                return new ResultVO("注册成功");
            } else {
                throw new BusinessException("验证码错误");
            }
        }

        return ResultVO.success("注册成功");
    }


    /**
     * refresh_token 过期时间30天
     */
    private static final long EXPIRE_TIME_THIRTY_DAY = 7 * 24 * 60 * 60 * 1000;

    /**
     * access_token 过期时间7天
     */
    private static final long EXPIRE_TIME_SEVEN_DAY = 7 * 24 * 60 * 60 * 1000;

    @PostMapping(value = "account/login")
    @ApiOperation(value = "登录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", paramType = "query", required = true),
            @ApiImplicitParam(name = "password", value = "密码", paramType = "query", required = false),
            @ApiImplicitParam(name = "code", value = "短信验证码", paramType = "query", required = false),
            @ApiImplicitParam(name = "loginType", value = "登录类型", paramType = "query", required = true, dataTypeClass = LoginTypeEnum.class)
    })
    public ResultVO<AppLoginVO> login(
            @RequestParam("username") String username,
            @RequestParam("password") String password,
            @RequestParam("loginType") LoginTypeEnum loginType,
            @RequestParam(value = "code", required = false) String code,
            HttpServletRequest request
    ) throws BusinessException {

        //TODO 为了防止用户暴力破解这里需要把反复尝试的ip拉入黑名单
        log.info("用户 {} 尝试登录", username);
        String requestIP = WebRequestUtil.getIpAddr(request);
        if (StringUtils.isEmpty(password) && StringUtils.isEmpty(code)) {
            throw new BusinessException("短信验证码或密码登陆必须选择一项");
        }


        UserAuthEntity userAuthEntity = userAuthService.selectOne(UserAuthEntity.builder().loginAccount(username).build());

        if (ObjectUtils.isEmpty(userAuthEntity)) {

            throw new BusinessException("用户名或密码错误");

        } else if (!StringUtils.isEmpty(code) && !smsService.verifyCode(username, code)) {

            userAuthService.updateLoginInfoFail(userAuthEntity, requestIP);
            throw new BusinessException("验证码错误");

        } else if (!StringUtils.isEmpty(password) && !passwordHelper.verifyPassword(password, userAuthEntity.getLoginAccount(), userAuthEntity.getLoginSalt(), userAuthEntity.getLoginPassword())) {

            userAuthService.updateLoginInfoFail(userAuthEntity, requestIP);
            throw new BusinessException("用户名或密码错误");

        } else {

            // 登陆成功

            String accessToken;
            Date accessExpireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME_SEVEN_DAY);
            String loginDevice = getOsAndBrowserInfo(request);
            Long userId = userAuthEntity.getUserInfoId();

            String refreshToken;
            Date refreshTokenExpireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME_THIRTY_DAY);

            //签发Token，更新登录信息

            accessToken = JWTUtil.signAccessToken(userAuthEntity.getId(), passwordHelper.encryptPassword(password, userAuthEntity.getLoginAccount(), userAuthEntity.getLoginSalt()), accessExpireDate);
            refreshToken = JWTUtil.signRefreshToken(accessToken, passwordHelper.encryptPassword(password, userAuthEntity.getLoginAccount(), userAuthEntity.getLoginSalt()), refreshTokenExpireDate);


            // 更新登录成功信息
            userAuthService.updateLoginInfoSuccess(userAuthEntity, requestIP, loginDevice, accessToken);

            // 返回信息
            String lastLoginPlace;
            lastLoginPlace = userAuthEntity.getLastLoginIp();
            if (StringUtils.isNotEmpty(lastLoginPlace)) {
                lastLoginPlace = GetPlaceUtil.getPlace(lastLoginPlace) + "(" + lastLoginPlace + ")";
            }

            return new ResultVO<>(
                    AppLoginVO.builder()
                            .token(accessToken)
                            .refreshToken(refreshToken)
                            .lastLoginTime(DateUtil.formatTime(userAuthEntity.getLastLoginTime(), "yyyy年MM月dd日 HH:mm:ss"))
                            .lastLoginPlace(lastLoginPlace)
                            .expireTime(DateUtil.formatTime(accessExpireDate, "yyyy年MM月dd日 HH:mm:ss"))
                            .build()
            );
        }
    }


    @PostMapping("token/refresh")
    @ApiOperation(value = "刷新token")
    public ResultVO<AppLoginVO> refreshToken(String refreshToken, String token, HttpServletRequest request) {
        //TODO 为了防止用户暴力破解这里需要把反复尝试的ip拉入黑名单

        String requestIP = WebRequestUtil.getIpAddr(request);

        String lastToken = JWTUtil.getLastTokenFromRefreshToken(refreshToken);
        // 验证refresh_token 和 用户当前使用的token一致
        if (StringUtils.isEmpty(token) || !token.equals(lastToken)) {
            log.info("token {}", token);
            throw new BusinessException("无效操作");
        }

        UserAuthEntity userAuthEntity = userAuthService.selectByPrimaryKey(JWTUtil.getUserAuthId(token));

        log.info("用户 {} 尝试获取新token", userAuthEntity.getLoginAccount());

        String newAccessToken;
        Date accessExpireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME_SEVEN_DAY);
        String loginDevice = getOsAndBrowserInfo(request);
        Long userId = userAuthEntity.getUserInfoId();

        String newRefreshToken;
        Date refreshTokenExpireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME_THIRTY_DAY);

        //签发新Token，更新登录信息

        newAccessToken = JWTUtil.signAccessToken(userAuthEntity.getId(), userAuthEntity.getLoginPassword(), accessExpireDate);
        newRefreshToken = JWTUtil.signRefreshToken(newAccessToken, userAuthEntity.getLoginPassword(), refreshTokenExpireDate);

        // 返回信息
        String lastLoginPlace;
        lastLoginPlace = userAuthEntity.getLastLoginIp();
        if (StringUtils.isNotEmpty(lastLoginPlace)) {
            lastLoginPlace = GetPlaceUtil.getPlace(lastLoginPlace) + "(" + lastLoginPlace + ")";
        }


        return new ResultVO<>(
                AppLoginVO.builder()
                        .token(newAccessToken)
                        .refreshToken(newRefreshToken)
                        .lastLoginTime(DateUtil.formatTime(userAuthEntity.getLastLoginTime(), "yyyy年MM月dd日 HH:mm:ss"))
                        .lastLoginPlace(lastLoginPlace)
                        .expireTime(DateUtil.formatTime(accessExpireDate, "yyyy年MM月dd日 HH:mm:ss"))
                        .build()
        );
    }

}
